package com.lanchetech.user.service.impl;


import com.alibaba.excel.util.CollectionUtils;
import com.alibaba.fastjson.JSON;
import com.baidu.fsg.uid.UidGenerator;
import com.lanchetech.bean.dto.WithdrawConfigDTO;
import com.lanchetech.bean.request.FreezeAmountReq;
import com.lanchetech.bean.request.ListByTimeReq;
import com.lanchetech.bean.request.WithdrawReq;
import com.lanchetech.bean.response.BasePageResp;
import com.lanchetech.bean.response.BaseResp;
import com.lanchetech.bean.response.ResultData;
import com.lanchetech.bean.response.WithdrawResp;
import com.lanchetech.bean.util.PageHelpUtil;
import com.lanchetech.bean.vo.*;
import com.lanchetech.common.enums.*;
import com.lanchetech.dao.*;
import com.lanchetech.entity.*;
import com.lanchetech.service.DaoService;
import com.lanchetech.user.config.WeChatPayProperties;
import com.lanchetech.user.service.FundService;
import com.github.binarywang.wxpay.bean.entpay.EntPayRequest;
import com.github.binarywang.wxpay.bean.entpay.EntPayResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class FundServiceImpl implements FundService {
    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    WithdrawMapper withdrawMapper;

    @Autowired
    AlipayAccountMapper alipayAccountMapper;

    @Autowired
    BankAccountMapper bankAccountMapper;

    @Autowired
    WebConfigMapper webConfigMapper;

    @Autowired
    UserMapper userMapper;

    @Autowired
    WxPayService wxPayService;

    @Autowired
    private WeChatPayProperties properties;

    @Autowired
    UserRelationshipMapper userRelationshipMapper;

    @Autowired
    BalanceFlowMapper balanceFlowMapper;

    @Autowired
    InvoiceApplyMapper invoiceApplyMapper;

    @Resource
    UidGenerator uidGenerator;

    @Autowired
    UserCommissionMapper userCommissionMapper;

    @Autowired
    HipoConfigMapper hipoConfigMapper;

    @Autowired
    DaoService daoService;

    @Autowired
    CardFlowMapper cardFlowMapper;

    @Autowired
    FreezeAmountMapper freezeAmountMapper;

    // 环境变量
    @Value("${env}")
    private String env;

    @Override
    public ResultData<BasePageResp<WithdrawResp>> getWithdrawPage(User user, WithdrawReq req) {
        req.setUserId(user.getId());
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        List<WithdrawResp> list = withdrawMapper.findAllPage(req);
        BigDecimal sum = withdrawMapper.sumAmountByTime(req);
        list.stream().forEach(item -> item.setAmountTotal(sum));
        return PageHelpUtil.buildPage(list, page);
    }

    @Override
    public BaseResp editAlipayAccount(User user, AlipayAccount alipayAccount) {
        if (alipayAccount.getId() == null) {
            // 创建支付宝账号
            alipayAccount.setUserId(user.getId());
            alipayAccount.setCreatedAt(new Date());
            alipayAccount.setUpdatedAt(new Date());
            alipayAccountMapper.insert(alipayAccount);
        } else {
            // 编辑支付宝账号
            AlipayAccount result = alipayAccountMapper.selectByPrimaryKey(alipayAccount.getId());
            // 判断是否是本人的支付宝账号
            if (result.getUserId().equals(user.getId())) {
                alipayAccount.setUserId(user.getId());
                alipayAccount.setUpdatedAt(new Date());
                alipayAccountMapper.updateByPrimaryKeySelective(alipayAccount);
            } else {
                return new BaseResp(ResultCode.FAIL);
            }
        }
        return new BaseResp();
    }

    @Override
    public ResultData<AlipayAccount> getAlipayAccount(User user) {
        return new ResultData<>(alipayAccountMapper.findOneByUserId(user.getId()));
    }

    @Override
    public BaseResp editBankAccount(User user, BankAccount bankAccount) {
        if (bankAccount.getId() == null) {
            // 一个人目前只能添加一张银行卡
            BankAccount result = bankAccountMapper.findOneByUserId(user.getId());
            if (result != null) {
                return new BaseResp(ResultCode.ONLY_ONE_BANK_CARD);
            }
            // 添加银行卡
            bankAccount.setUserId(user.getId());
            bankAccount.setCreatedAt(new Date());
            bankAccount.setUpdatedAt(new Date());
            bankAccountMapper.insert(bankAccount);
        } else {
            BankAccount result = bankAccountMapper.selectByPrimaryKey(bankAccount.getId());
            // 判断是否是本人的银行卡
            if (result.getUserId().equals(user.getId())) {
                bankAccount.setUserId(user.getId());
                bankAccount.setUpdatedAt(new Date());
                bankAccountMapper.updateByPrimaryKeySelective(bankAccount);
            } else {
                return new BaseResp(ResultCode.FAIL);
            }
        }
        return new BaseResp();
    }

    @Override
    public ResultData<BankAccount> getBankAccount(User user) {
        return new ResultData<>(bankAccountMapper.findOneByUserId(user.getId()));
    }

    @Override
    public BaseResp deleteBankAccount(User user, Long id) {
        bankAccountMapper.deleteByIdAndUserId(id, user.getId());
        return new BaseResp();
    }

    @Override
    public ResultData<List<BankAccount>> getBankAccountList(User user) {
        return new ResultData<>(bankAccountMapper.findAllByUserId(user.getId()));
    }

    @Override
    public BaseResp withdrawToAccount(User user, WithdrawVO req, HttpServletRequest httpServletRequest) {


        // 目前先强制使用安全密码
        if (user.getPayPassword() == null) {
            return new BaseResp(ResultCode.NOT_HAS_PAY_PASSWORD);
        }
        if (!new BCryptPasswordEncoder().matches(req.getVerifyToken(), user.getPayPassword())) {
            return new BaseResp(ResultCode.PAY_PASSWORD_ERROR);
        }

        Withdraw withdrawReq = req.getWithdraw();

        BigDecimal processingAmount = withdrawMapper.sumByUserIdAndStatus2(user.getId(), WithdrawStatusEnum.PROCESSING.getStatus());
        if (processingAmount == null) {
            processingAmount = BigDecimal.ZERO;
        }
        // 余额要大于提现金额
        if (user.getPoint().subtract(processingAmount).compareTo(withdrawReq.getAmount()) >= 0) {

            // 针对用户提现和商家提现，分别判断余额
            if (BusinessTypeEnum.USER.getType().equals(withdrawReq.getBusinessType())) {
                BigDecimal issue = userCommissionMapper.sumByUserIdAndBusinessTypeAndStatus(user.getId(), BusinessTypeEnum.USER.getType(), UserCommissionStatusEnum.ISSUED.getStatus());
                BigDecimal amount = withdrawMapper.sumByUserIdAndStatusAndBusinessType(user.getId(), WithdrawStatusEnum.SUCCESS.getStatus(), BusinessTypeEnum.USER.getType());

                if (amount == null) {
                    amount = BigDecimal.ZERO;
                }

                if (issue == null || issue.subtract(amount).compareTo(req.getWithdraw().getAmount()) < 0) {
                    return new BaseResp(ResultCode.INSUFFICIENT_BALANCE);
                }
            }

            if (BusinessTypeEnum.SHOP.getType().equals(withdrawReq.getBusinessType())) {
                BigDecimal issue = userCommissionMapper.sumByUserIdAndBusinessTypeAndShopIdAndStatus(user.getId(), BusinessTypeEnum.SHOP.getType(), withdrawReq.getShopId(), UserCommissionStatusEnum.ISSUED.getStatus());
                BigDecimal amount = withdrawMapper.sumByUserIdAndStatusAndBusinessTypeAndShopId(user.getId(), WithdrawStatusEnum.SUCCESS.getStatus(), BusinessTypeEnum.SHOP.getType(), withdrawReq.getShopId());
                if (amount == null) {
                    amount = BigDecimal.ZERO;
                }
                if (issue == null || issue.subtract(amount).compareTo(req.getWithdraw().getAmount()) < 0) {
                    return new BaseResp(ResultCode.INSUFFICIENT_BALANCE);
                }
            }

            WebConfig config = webConfigMapper.findOneByName(WebConfigEnum.WITHDRAW_SETTING.getConfig());
            WithdrawConfigDTO withdrawConfig = JSON.parseObject(config.getValue(), WithdrawConfigDTO.class);
            // 检查是否达到最低起提金额
            if (withdrawConfig.getMin().compareTo(withdrawReq.getAmount()) > 0) {
                return new BaseResp(ResultCode.NOT_ENOUGH_TO_START_WITHDRAW);
            } else {
                // 检查是否超过每日最高直接提现额 TODO::现在默认都要审核，所以不用判断最高提现额
                BigDecimal total = withdrawMapper.sumAmountOfDay(user.getId());
                if (total == null) {
                    total = new BigDecimal(0);
                }

                if (WithdrawTypeEnum.WECHAT.getPayway().equals(withdrawReq.getType()) && withdrawReq.getAmount().compareTo(withdrawConfig.getMax()) > 0) {
                    return new BaseResp(ResultCode.OVER_MAX_WITHDRAW_A_DAY);
                }

                if (WithdrawTypeEnum.WECHAT.getPayway().equals(withdrawReq.getType()) && user.getOpenid() == null) {
                    return new BaseResp(ResultCode.DO_NOT_HAVE_OPENID);
                }

                Withdraw withdraw = new Withdraw();
                withdraw.setUserId(user.getId());
//                // 当前只能手动支付宝打款
//                AlipayAccount alipayAccount = alipayAccountMapper.findOneByUserId(user.getId());
//                if (alipayAccount == null) {
//                    return new BaseResp(ResultCode.NOT_HAVE_ALIPAY_ACCOUNT);
//                }

                // 当前只能手动打到银行卡
                BankAccount bankAccount = bankAccountMapper.findOneByUserId(user.getId());
                if (bankAccount == null) {
                    return new BaseResp(ResultCode.NOT_HAVE_BANK_CARD);
                }


                BigDecimal taxRate = daoService.getHipoConfigNumber(HipoConfigEnum.TAX_RATE.getConfig());
                BigDecimal taxCountRate = daoService.getHipoConfigNumber(HipoConfigEnum.TAX_RATE.getConfig()).add(BigDecimal.ONE);
                BigDecimal serviceRate = daoService.getHipoConfigNumber(HipoConfigEnum.SERVICE_FEE_RATE.getConfig());
                BigDecimal taxAmount = withdrawReq.getAmount().subtract(withdrawReq.getAmount().divide(taxCountRate, 2, BigDecimal.ROUND_HALF_DOWN));
                BigDecimal service = BigDecimal.ZERO;


                withdraw.setType(WithdrawTypeEnum.BANK.getPayway());
                withdraw.setAccount(bankAccount.getAccount());
                withdraw.setAmount(withdrawReq.getAmount());
                // 待提现，提现进行中
                withdraw.setStatus(WithdrawStatusEnum.PROCESSING.getStatus());
                withdraw.setCreatedAt(new Date());
                withdraw.setUpdatedAt(new Date());
                withdraw.setBusinessType(withdrawReq.getBusinessType());
                withdraw.setShopId(withdrawReq.getShopId());
                withdraw.setTaxRate(taxRate);
                withdraw.setServiceRate(serviceRate);
                withdraw.setTax(taxAmount);
                withdraw.setService(service);
                withdraw.setFinalAmount(withdrawReq.getAmount().subtract(taxAmount).subtract(service));
                withdrawMapper.insert(withdraw);

                // TODO:: 当前只提供支付宝提现，以下微信提现逻辑暂时关闭
//                if (withdrawConfig.getDayLimitAmount().compareTo(req.getAmount().add(total)) >= 0) {
//                    // 发起微信提现
//                    return this.withdrawToWechat(withdraw, user, IpUtil.getIpAddr(httpServletRequest));
//                }

                // 超出提现额了, 不做处理，直接是PROCESSING，待处理状态
                return new BaseResp();
            }
        } else {
            return new BaseResp(ResultCode.INSUFFICIENT_BALANCE);
        }
    }


    /**
     * 提现到微信零钱
     *
     * @param withdraw
     */

    // TODO 还没检查逻辑
    private BaseResp withdrawToWechat(Withdraw withdraw, User user, String ip) {
        WxPayService payService = wxPayService;
        // 进行微信提现
        // 执行提现, 用户的余额在外部做检查，在这里已经默认余额充足
        Date nowTime = new Date(System.currentTimeMillis());
        SimpleDateFormat sdFormatter = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        //订单号由用户操作时间毫秒数+用户id实现的
        String outBizNo = sdFormatter.format(nowTime) + user.getId();
        EntPayRequest entPayRequest = new EntPayRequest();
        entPayRequest.setAmount(withdraw.getAmount().multiply(new BigDecimal(100)).intValue());
        entPayRequest.setAppid(this.properties.getAppId());
        entPayRequest.setDescription("顺程提现");
        entPayRequest.setMchId(this.properties.getMchId());
        entPayRequest.setOpenid(user.getOpenid());
        entPayRequest.setCheckName("NO_CHECK");
        entPayRequest.setPartnerTradeNo(outBizNo);
        entPayRequest.setSpbillCreateIp(ip);

        EntPayResult entPayResult = null;
        // 只有生产环境能真实提现，其他环境，直接按提现成功处理，但不给钱。
        if ("production".equals(env)) {
            try {
                entPayResult = payService.getEntPayService().entPay(entPayRequest);
                log.info("微信提现到零钱成功：{}", entPayResult);
            } catch (WxPayException e) {
                log.error("微信提现到零钱失败：{}", e);
                return new BaseResp(ResultCode.FAIL);
            }
        }

        userMapper.updatePointById(user.getPoint().subtract(withdraw.getAmount()), user.getId());

        // 创建资金流水
        balanceFlowMapper.insert(BalanceFlow.builder()
                .userId(user.getId())
                .amount(withdraw.getAmount())
                .tradeType(TradeTypeEnum.WITHDRAW.getTradeType())
                .type(TradeTypeEnum.WITHDRAW.getType())
                .point(user.getPoint().subtract(withdraw.getAmount()))
                .relateId(withdraw.getId())
                .remark("提现")
                .createdAt(new Date())
                .build());


        withdraw.setAccount(user.getOpenid());
        withdraw.setType(WithdrawTypeEnum.WECHAT.getPayway());
        withdraw.setStatus(WithdrawStatusEnum.SUCCESS.getStatus());
        withdraw.setUpdatedAt(nowTime);
        withdrawMapper.updateByPrimaryKey(withdraw);
        return new BaseResp();
    }

    @Override
    public ResultData<CardFlowUsedSum> getCardFlowUsedSum(User user) {
        BigDecimal cardFlowUsedSum = cardFlowMapper.getUsedSumByUserId(user.getId(), CardFlowTypeEnum.PAY.getType());
        CardFlowUsedSum result = new CardFlowUsedSum();
        result.setCardFlowUsedSum(cardFlowUsedSum);
        return new ResultData<>(result);
    }

    @Override
    public ResultData<BasePageResp<FreezeAmountVO>> getFreezeAmountPage(FreezeAmountReq req) {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        req.setTenantId(user.getShopId());
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        List<FreezeAmountVO> list = freezeAmountMapper.findAllPage(req);
        if (!CollectionUtils.isEmpty(list)) {
            Map<Long, Orders> ordersMap = daoService.getOrdersMap(list.stream().map(FreezeAmountVO::getOrderId).collect(Collectors.toList()));
            Map<Long, User> userMap = daoService.getUserMap(ordersMap.values().stream().map(Orders::getUserId).collect(Collectors.toList()));
            list.stream().forEach(item -> {
                Orders orders = ordersMap.get(item.getOrderId());
                item.setOrderRemark(orders.getRemark());
                item.setUserNickname(userMap.get(orders.getUserId()).getNickname());
                item.setUserAvatar(userMap.get(orders.getUserId()).getAvatar());
            });
        }
        ResultData<BasePageResp<FreezeAmountVO>> result = PageHelpUtil.buildPage(list, page);
        result.getValue().setExtra(freezeAmountMapper.sumAmountStatistics(req));
        return result;
    }
}